package visualisation;

import java.awt.BorderLayout;
import java.awt.Color;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.Box;
import javax.swing.JPanel;
import javax.swing.JSplitPane;

import main.Main;

import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.Action;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.FocusControl;
import prefuse.controls.NeighborHighlightControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.render.PolygonRenderer;
import prefuse.render.Renderer;
import prefuse.util.ColorLib;
import prefuse.util.force.DragForce;
import prefuse.util.force.ForceSimulator;
import prefuse.util.force.NBodyForce;
import prefuse.util.force.SpringForce;
import prefuse.util.ui.JForcePanel;
import prefuse.util.ui.UILib;
import prefuse.visual.AggregateItem;
import prefuse.visual.AggregateTable;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;
import tools.Fragment;
import agent.FragmentAgent;

public class ProcessView extends JPanel {
    
    public static final String GRAPH = "graph";
    public static final String NODES = "graph.nodes";
    public static final String EDGES = "graph.edges";
    public static final String AGGR = "aggregates";

    private static Visualization m_vis;
    private static VisualGraph vg;
    //private static DataColorAction colorisation;
    
    private static int [] paletteNodes;
    
    public ProcessView(Graph g, String label) {
    	super(new BorderLayout());
    	
        // create a new, empty visualization for our data
        m_vis = new Visualization();
                
        // --------------------------------------------------------------------
        // set up the renderers
        
     // draw aggregates as polygons with curved edges
        Renderer polyR = new PolygonRenderer(Constants.POLY_TYPE_CURVE);
        ((PolygonRenderer)polyR).setCurveSlack(0.15f);
        
        LabelRenderer tr = new LabelRenderer();
        tr.setTextField(label);
        tr.setRoundedCorner(8, 8);
        
        DefaultRendererFactory drf = new DefaultRendererFactory(tr);
        drf.add("ingroup('aggregates')", polyR);
        m_vis.setRendererFactory(drf);

        // --------------------------------------------------------------------
        // register the data with a visualization
        
        // adds graph to visualization and sets renderer label field
        setGraph(g, label);
        
        // fix selected focus nodes
        TupleSet focusGroup = m_vis.getGroup(Visualization.FOCUS_ITEMS); 
        focusGroup.addTupleSetListener(new TupleSetListener() {
            public void tupleSetChanged(TupleSet ts, Tuple[] add, Tuple[] rem)
            {
               for ( int i=0; i<rem.length; ++i )
                    ((VisualItem)rem[i]).setFixed(false);
                for ( int i=0; i<add.length; ++i ) {
                    ((VisualItem)add[i]).setFixed(false);
                    ((VisualItem)add[i]).setFixed(true);
                }
                if ( ts.getTupleCount() == 0 ) {
                    ts.addTuple(rem[0]);
                    ((VisualItem)rem[0]).setFixed(false);
                }

            	
                m_vis.run("draw");
            }
        });
        
        
        
        // --------------------------------------------------------------------
        // create actions to process the visual data
        

        paletteNodes=new int[]{
        		Color.TRANSLUCENT,ColorLib.color(Color.BLUE),ColorLib.color(Color.RED),ColorLib.color(Color.GREEN)
            };
        
        int [] paletteNodesFill=new int[]{
        		Color.TRANSLUCENT,ColorLib.rgb(200,200,255),ColorLib.rgb(200,200,255),ColorLib.rgb(200,200,255)
        }; 
        
        int [] paletteEdges=new int[]{
        		ColorLib.color(Color.BLACK),Color.TRANSLUCENT
        };
        
        ColorAction fill = new DataColorAction(NODES, "color", Constants.NOMINAL,VisualItem.FILLCOLOR,paletteNodesFill);
        fill.add(VisualItem.FIXED, ColorLib.rgb(255,100,100));
        fill.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255,200,125));
        
        ColorAction aFill = new ColorAction(AGGR, VisualItem.FILLCOLOR, ColorLib.gray(240));
        ColorAction aStroke = new ColorAction(AGGR, VisualItem.STROKECOLOR);
        
        aStroke.setDefaultColor(ColorLib.gray(200));
        aStroke.add("_hover", ColorLib.rgb(255,100,100));
        
        ActionList redraw = new ActionList();
        redraw.add(fill);
 
        redraw.add(new DataColorAction(NODES, "color", Constants.NOMINAL,VisualItem.STROKECOLOR,paletteNodes));
        redraw.add(new ColorAction(NODES, VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0)));
        
        redraw.add(new DataColorAction(EDGES,"invisible", Constants.NOMINAL, VisualItem.FILLCOLOR, paletteEdges));
        redraw.add(new DataColorAction(EDGES,"invisible", Constants.NOMINAL, VisualItem.STROKECOLOR, paletteEdges));


        redraw.add(new RepaintAction());
        
        m_vis.putAction("redraw", redraw);
        
        
        ActionList draw = new ActionList();
        draw.add(fill);
 
        draw.add(new DataColorAction(NODES, "color", Constants.NOMINAL,VisualItem.STROKECOLOR,paletteNodes));
        draw.add(new ColorAction(NODES, VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0)));
        
        draw.add(new DataColorAction(EDGES,"invisible", Constants.NOMINAL, VisualItem.FILLCOLOR, paletteEdges));
        draw.add(new DataColorAction(EDGES,"invisible", Constants.NOMINAL, VisualItem.STROKECOLOR, paletteEdges));


        draw.add(new RepaintAction());
        
        ActionList animate = new ActionList(Activity.INFINITY);
        
        
        //Force gestion
        ForceSimulator force=new ForceSimulator();
        force.addForce(new DragForce());
        force.addForce(new SpringForce(SpringForce.DEFAULT_MIN_SPRING_COEFF, SpringForce.DEFAULT_SPRING_LENGTH));
        force.addForce(new NBodyForce(NBodyForce.DEFAULT_GRAV_CONSTANT,50f,NBodyForce.DEFAULT_THETA));
        
        
        /**/
        force.addForce(new SpringGravForce());
        
        
        ForceDirectedLayout forceLayout = new ForceDirectedLayout(GRAPH);
        forceLayout.setForceSimulator(force);
        
        animate.add(forceLayout);
        animate.add(new AggregateLayout(AGGR));
        animate.add(fill);
        
        animate.add(aFill);
        animate.add(aStroke);
        animate.add(new RepaintAction());
        
        // finally, we register our ActionList with the Visualization.
        // we can later execute our Actions by invoking a method on our
        // Visualization, using the name we've chosen below.
        m_vis.putAction("draw", draw);
        m_vis.putAction("layout", animate);

        m_vis.runAfter("draw", "layout");
        
        
        // --------------------------------------------------------------------
        // set up a display to show the visualization
        
        Display display = new Display(m_vis);
        display.setSize(700,700);
        display.pan(350, 350);
        display.setForeground(Color.GRAY);
        display.setBackground(Color.WHITE);
        
        display.setHighQuality(!Main.lowPerf);
        
        // main display controls
        display.addControlListener(new FocusControl(1));
        display.addControlListener(new AggregateDragControl());
        display.addControlListener(new PanControl());
        display.addControlListener(new ZoomControl());
        display.addControlListener(new WheelZoomControl());
        display.addControlListener(new ZoomToFitControl());
        display.addControlListener(new NeighborHighlightControl());
        
        // --------------------------------------------------------------------        
        // launch the visualization
        
        // create a panel for editing force values
        
        JForcePanel fpanel = new JForcePanel(force);
        
        fpanel.add(Box.createVerticalGlue());
        
        // create a new JSplitPane to present the interface
        JSplitPane split = new JSplitPane();
        split.setLeftComponent(display);
        split.setRightComponent(fpanel);
        split.setOneTouchExpandable(true);
        split.setContinuousLayout(false);
        split.setDividerLocation(700);
        split.setResizeWeight(1.0);


        
        // now we run our action list
        m_vis.run("draw");
        
        add(split);
    }
    
    public void setGraph(Graph g, String label) {
        // update labeling
        DefaultRendererFactory drf = (DefaultRendererFactory)
                                                m_vis.getRendererFactory();
        ((LabelRenderer)drf.getDefaultRenderer()).setTextField(label);
        
        // update graph
        m_vis.removeGroup(GRAPH);
        vg = m_vis.addGraph(GRAPH, g);
        m_vis.setValue(EDGES, null, VisualItem.INTERACTIVE, Boolean.FALSE);
        
    }
    
    
private static void initDataGroups() {
        
        // add visual data groups
        //VisualGraph vg = m_vis.addGraph(GRAPH, currentGraph);
        //m_vis.setInteractive(EDGES, null, false);
        //m_vis.setValue(NODES, null, VisualItem.SHAPE, new Integer(Constants.SHAPE_ELLIPSE));
        
        AggregateTable at = m_vis.addAggregates(AGGR);
        at.addColumn(VisualItem.POLYGON, float[].class);
        //at.addColumn("id", int.class);
        
        // add nodes to aggregates
        // create an aggregate for each 3-clique of nodes
        Iterator nodes = vg.nodes(); 
        AggregateItem aitem = (AggregateItem)at.addItem();
            //aitem.setInt("id", 1);
            for ( int j=0; j<4; ++j ) {
                aitem.addItem((VisualItem)nodes.next());
            }
    }
      
    private static Graph currentGraph;
    private static ProcessView currentView;
      
    private static HashMap<Fragment, Node> refNode= new HashMap<Fragment, Node>();
    private static HashMap<Fragment,Vector<Edge>> currentEdges = new HashMap<Fragment, Vector<Edge>>();
    private static HashMap<Edge, Vector<Fragment>> crossRefEdges = new HashMap<Edge, Vector<Fragment>>();
    
    private static long mainProcess;
      
    public static JPanel visualise(String label, Vector<Fragment> fragList){
	  
      	UILib.setPlatformLookAndFeel();
      	
      	currentGraph = new Graph(true);
      	      	
      	Table nodes=currentGraph.getNodeTable();
      	Table edges=currentGraph.getEdgeTable();
      	
      	
      	nodes.addColumn("name", String.class);
      	nodes.addColumn("color", Long.class);
      	edges.addColumn("invisible", Boolean.class,true);
  
      	
      	currentGraph.addNode();
      	nodes.set(0,"name","not part of a process");
      	nodes.set(0, "color", 0l);
      	
      	currentGraph.addNode();
      	nodes.set(1,"name","main process");
      	nodes.set(1, "color", -1l);
      	
      	currentGraph.addNode();
      	nodes.set(2,"name","alternate process");
      	nodes.set(2, "color", 1l);
    	
      	currentGraph.addNode();
      	nodes.set(3,"name","legend");
      	nodes.set(3, "color", -2l);
      	
      	currentGraph.addEdge(0, 1);
      	currentGraph.addEdge(1, 2);
      	currentGraph.addEdge(2, 0);
      	
      	currentGraph.addEdge(3, 0);
      	currentGraph.addEdge(3, 1);
      	currentGraph.addEdge(3, 2);
      	
    	
        currentView=new ProcessView(currentGraph, label);
             	         
        initDataGroups();
        
        return currentView;
    }
    
    private static long getColor(long process){
    	if(process == mainProcess) return -1l;
    	
    	if(process != 0l) return 1l;
    	  	
    	return process;
    }
           
    public static void reloadGraph(HashMap<Fragment,HashSet<Fragment>> dependencies){
    	  
    	  if(currentGraph!=null){
    	  
    	  Action layoutTemp = m_vis.getAction("layout");
    	  m_vis.cancel("layout");
    	  
    	  Action drawTemp = m_vis.getAction("draw");
    	  m_vis.cancel("draw");
    	  
    	  
    	  
    	  //find main process
    	  int stimMax = Integer.MIN_VALUE;
    	  
    	  mainProcess=1;
    	  for(Fragment f : dependencies.keySet()){
    		  if(f.getProcessNumber() > 0 && f.getCriticity() > stimMax){
    			  stimMax=f.getCriticity();
    			  mainProcess=f.getProcessNumber();
    		  }
		  	
    	  }
    	  

    	  //remove disconnected nodes and check for colors
    	  HashMap<Fragment, Node> temp=new HashMap<Fragment, Node>(refNode);
    	  
    	  for(Fragment f : temp.keySet()){
    		  if (!dependencies.containsKey(f)){
    			  for(Edge e : currentEdges.get(f)){
    				  for(Fragment f1 : crossRefEdges.get(e)){
    					  if(f1!=f) currentEdges.get(f1).remove(e);
    				  }
    				  crossRefEdges.remove(e);
    			  }
    		  	currentEdges.remove(f);
    		  	currentGraph.removeNode(refNode.get(f));
    		  	refNode.remove(f);
    		  	
    		  }
    		  else{		  
    			  refNode.get(f).set("color", getColor(f.getProcessNumber()));
    		  }
    	  }
    	  
    	  //add new nodes
    	  for(Fragment f : dependencies.keySet()){
    		  if(!refNode.containsKey(f)){
    			  
    			  refNode.put(f, currentGraph.addNode());
    			  refNode.get(f).set("name", f.getId());
    			  refNode.get(f).set("color", getColor(f.getProcessNumber()));
    			  
    			  currentEdges.put(f, new Vector<Edge>());

    		  }
    	  }
    	  
    	  //add new edges
    	  for(Fragment f : dependencies.keySet()){
    		  for(Fragment p : dependencies.get(f)){
    			  if(currentGraph.getEdge(refNode.get(p), refNode.get(f)) == null){
    				  currentGraph.addEdge(refNode.get(p), refNode.get(f));
    				  
    				  currentGraph.getEdge(refNode.get(p), refNode.get(f)).set("invisible", false);
    				  
    				  currentEdges.get(f).add(currentGraph.getEdge(refNode.get(p), refNode.get(f)));
    				  currentEdges.get(p).add(currentGraph.getEdge(refNode.get(p), refNode.get(f)));
    				  crossRefEdges.put(currentGraph.getEdge(refNode.get(p), refNode.get(f)), new Vector<Fragment>());
    				  crossRefEdges.get(currentGraph.getEdge(refNode.get(p), refNode.get(f))).add(f);
    				  crossRefEdges.get(currentGraph.getEdge(refNode.get(p), refNode.get(f))).add(p);
    			  }
    		  }
		  }
    	  
    	  
    	  //reorder color table
          paletteNodes=new int[]{
              	ColorLib.color(Color.BLUE), ColorLib.color(Color.RED), ColorLib.color(Color.GREEN)
              };

   
    	  
    	  m_vis.putAction("layout", layoutTemp);
    	  m_vis.putAction("draw", drawTemp);
    	  m_vis.runAfter("redraw", "layout");
  
    	  m_vis.run("redraw");
    	  
    	  }

    } 

}
